'''
Data model for the ShapeConfig.xml file.
Creates itself from the DOM of the parsed xml file.

Licensed under the GPL.

@author: screwdriver
'''

'''
Version history:
----------------
2009-03-21 screwdirver Created.
2009-03-23 screwdirver Released version 1.0.0 
---
2009-03-26 screwdriver Implemented error handling.
2009-03-27 screwdriver Refactoring: created factory class & made the other classes proper beans.
'''

import string;
import re;
import os;
from string import Template;
from shapeconfigexceptions import ShapeConfigException;
from xml.dom.minidom import parse;


MAJOR_VERSION = 1;
MINOR_VERSION = 0;
MAINTENANCE = 1;

VERSION = [MAJOR_VERSION,".", MINOR_VERSION,".", MAINTENANCE];

FILE_DOES_NOT_EXIST = "The file \"$file\" does not exist.";
NOT_A_FILE = "\"$file\" is not a file.";
CAN_NOT_LOAD_FILE = "Can not load file \"$file\"";

NO_NONE_VALUE_ALLOWED = "No None value allowed for $valueName";
ATTRIBUTE_NOT_FOUND = "Every $element must have a $attribute attribute.";

PARENT_DOCUMENT = "the document";
ELEMENT_NOT_FOUND = "The $element is missing in $parent.";
TOO_FEW_ELEMENTS = "At least $minimumNumber $element must be in $parent.";
TOO_MANY_ELEMENTS = "Only $maximumNumber $element element allowed in $parent.";

UNKNOWN_SHAPE_TYPE = "Unknown shape type \"$shapeType\" for shape file \"$shapeFileName\"";

__factoryInstance = None;

def getShapeConfigFactory():
	
	global __factoryInstance;
	
	if (__factoryInstance == None):
		__factoryInstance = ShapeConfigFactory();
	
	return __factoryInstance;

class ShapeConfigFactory:
	
	def __init__(self):
		pass;
	
	def load(self, file):
		
		if (not os.path.exists(file)):
			raise ShapeConfigException(Template(FILE_DOES_NOT_EXIST).safe_substitute(file=file));
		if (not os.path.isfile(file)):
			raise ShapeConfigException(Template(NOT_A_FILE).safe_substitute(file=file));
		
		try:
			dom = parse(file);
		except Exception, e:
			raise ShapeConfigException(repr(e));
		
		if (dom == None):
			raise ShapeConfigException(Template(CAN_NOT_LOAD_FILE).safe_substitute(file=file));
		
		
		shapeConfig = self.__createShapeConfig(dom);
	
		
		return shapeConfig;
	
	def __createShapeConfig(self, dom):
		shapeConfigElements = dom.getElementsByTagNameNS(ShapeConfig._SHAPE_CONFIG_NAMESPACE, ShapeConfig._SHAPE_CONFIG);
		
		self.__checkCardinality(shapeConfigElements, ShapeConfig._SHAPE_CONFIG, PARENT_DOCUMENT, 1, 1);
		
		shapeConfigElement = shapeConfigElements[0];
		
		shapeConfig = ShapeConfig();
		
		shapeFileElements = shapeConfigElement.getElementsByTagName(ShapeConfig._SHAPE_CONFIG_SHAPE_FILE);
		
		if (shapeFileElements != None):
			if (len(shapeFileElements) > 0):
				for shapeFileElement in shapeFileElements:
					shapeConfig.shapeFiles.append(self.__createShapeFile(shapeFileElement));
					
		return shapeConfig;
	
	
	def __checkCardinality(self, elements, elementName, parentName, minCardinality, maxCardinality):
		if (elements == None):
			raise ShapeConfigException(Template(ELEMENT_NOT_FOUND).safe_substitute(element=elementName, parent=parentName));
		
		if (len(elements) < minCardinality):
			raise ShapeConfigException(Template(TOO_FEW_ELEMENTS).safe_substitute(element=elementName, parent=parentName, minimumNumber=minCardinality));
		
		if (len(elements) > maxCardinality):
			raise ShapeConfigException(Template(TOO_MANY_ELEMENTS).safe_substitute(element=elementName, parent=parentName, maximumNumber=maxCardinality));
		
	def __createShapeFile(self, shapeFileElement):
		shapeFile = ShapeFile();
		
		shapeFile.name = self.__readAttribute(ShapeFile._SHAPE_FILE, ShapeFile._SHAPE_FILE_NAME, shapeFileElement);
		shapeFile.material = self.__readAttribute(ShapeFile._SHAPE_FILE, ShapeFile._SHAPE_FILE_MATERIAL, shapeFileElement);

		
		shapeTypeElements = shapeFileElement.getElementsByTagName(ShapeFile._SHAPE_FILE_SHAPE_TYPE);
		
		self.__checkCardinality(shapeTypeElements, ShapeFile._SHAPE_FILE_SHAPE_TYPE, ShapeFile._SHAPE_FILE, 1, 1);
		
		shapeTypeElement = shapeTypeElements[0];
		
		shapeFile.shapeType = self.__createShapeType(shapeTypeElement, shapeFile.name);
	
		return shapeFile;
		
		
	def __createShapeType(self, shapeTypeElement, shapeFileName):
		
		shapeTypeClass = shapeTypeElement.getAttributeNS("http://www.w3.org/2001/XMLSchema-instance", "type");
		
		shapeType = None;
		
		if (re.match(".*:Line$", shapeTypeClass)):
			shapeType = self.__createLine(shapeTypeElement);
		elif (re.match(".*:Shape$", shapeTypeClass)):
			shapeType = self.__createShape(shapeTypeElement);
		elif (re.match(".*:Point$", shapeTypeClass)):
			shapeType = self.__createPoint(shapeTypeElement);
		else:
			raise ShapeConfigException(Template(UNKNOWN_SHAPE_TYPE).safe_substitute(shapeType=shapeTypeClass, shapeFileName=shapeFileName));
			
		return shapeType;
	
	
	def __createLine(self, shapeTypeElement):
		line = Line();
		
		line.maxSegmentLength = self.__readAttribute(Line._LINE, Shape._SHAPE_MAX_SEGMENT_LENGTH, shapeTypeElement);
		
		line.width = self.__readAttribute(Line._LINE, Line._LINE_WIDTH, shapeTypeElement);
		
		return line;

	def __createShape(self, shapeTypeElement):
		shape = Shape();
		
		shape.maxSegmentLength = self.__readAttribute(Shape._SHAPE, Shape._SHAPE_MAX_SEGMENT_LENGTH, shapeTypeElement);
		
		return shape;

	def __createPoint(self, shapeTypeElement):
		point = Point();
		
		point.size = self.__readAttribute(Point._POINT, Point._POINT_SIZE, shapeTypeElement);

		return point;
	
	def __readAttribute(self, elementName, attributeName, element):
		
		value = element.getAttribute(attributeName);
		
		if(value == None or value == ""):
			raise ShapeConfigException(Template(ATTRIBUTE_NOT_FOUND).safe_substitute(element=elementName, attribute=attributeName));
		
		return value;


'''
ShapeConfig
'''
class ShapeConfig:
	
	_SHAPE_CONFIG_NAMESPACE = "http://shapeconfig/1.0"
	_SHAPE_CONFIG = "ShapeConfig";
	_SHAPE_CONFIG_SHAPE_FILE = "shapeFile";
	
	def shapeFiles(): #@NoSelf
		doc = """The list of ShapeFiles.""" #@UnusedVariable
		
		def fget(self):
			return self._shapeFiles
			
		def fset(self, value):
			self._shapeFiles = value
			
		def fdel(self):
			del self._shapeFiles
			
		return locals()
		
	shapeFiles = property(**shapeFiles())
	
	
	def __init__(self):
		self.shapeFiles = [];
	

'''
ShapeFile
'''
class ShapeFile:
	
	_SHAPE_FILE = "shapeFile";
	_SHAPE_FILE_PARENT = "ShapeConfig";
	_SHAPE_FILE_NAME = "name";
	_SHAPE_FILE_MATERIAL = "material";
	_SHAPE_FILE_SHAPE_TYPE = "shapeType";
	
	def name(): #@NoSelf
		doc = """Name of the shape file.""" #@UnusedVariable
		
		def fget(self):
			return self._name
			
		def fset(self, value):
			self._name = value
			
		def fdel(self):
			del self._name
			
		return locals()
		
	name = property(**name())
	
	def material(): #@NoSelf
		doc = """Material to assign to the shape.""" #@UnusedVariable
		
		def fget(self):
			return self._material
			
		def fset(self, value):
			self._material = value
			
		def fdel(self):
			del self._material
			
		return locals()
		
	material = property(**material())
	
	def shapeType(): #@NoSelf
		doc = """The ShapeType of the shape.""" #@UnusedVariable
		
		def fget(self):
			return self._shapeType
			
		def fset(self, value):
			self._shapeType = value
			
		def fdel(self):
			del self._shapeType
			
		return locals()
		
	shapeType = property(**shapeType())
	
	def __init__(self):
		pass;
	
	

'''
Shape
'''	
class Shape:
	
	_SHAPE = "Shape";
	_SHAPE_MAX_SEGMENT_LENGTH = "maxSegmentLength";

	def maxSegmentLength(): #@NoSelf
		doc = """The lenght of the longest straight segment in meters.""" #@UnusedVariable
		
		def fget(self):
			return self._maxSegmentLength
			
		def fset(self, value):
			self._maxSegmentLength = value
			
		def fdel(self):
			del self._maxSegmentLength
			
		return locals()
		
	maxSegmentLength = property(**maxSegmentLength())
	
	def __init__(self):
		pass;
	

'''
Line
'''	
class Line(Shape):
	
	_LINE = "Line";
	_SHAPE = _LINE;
	_LINE_WIDTH = "width";
	
	def width(): #@NoSelf
		doc = """The width of the line in meters.""" #@UnusedVariable
		
		def fget(self):
			return self._width
			
		def fset(self, value):
			self._width = value
			
		def fdel(self):
			del self._width
			
		return locals()
		
	width = property(**width())
	
	def __init__(self):
		pass;
		


'''
Point
'''	
class Point:
	
	_POINT = "Point";
	_POINT_SIZE = "size";
	
	def size(): #@NoSelf
		doc = """The size of the point in meters.""" #@UnusedVariable
		
		def fget(self):
			return self._size
			
		def fset(self, value):
			self._size = value
			
		def fdel(self):
			del self._size
			
		return locals()
		
	size = property(**size())
	
	def __init__(self):
		pass;

		
		